package com.lfy.java8.streamapi;


import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 测试 Stream API；
 *   Stream API： Java 8 中最激动人心的改进之一。
 *   这是一个允许你以【声明方式处理数据集合】（包括来自数组、集合如 List 或 Map 的元素）的工具。
 *   【声明式】：要做什么，用一句话描述做什么，不用写代码实现；
 *   【命令式】：要做什么，自己一行一行写代码实现；
 *
 *    // Stream API： 让你把所有的数据和对数据的加工类比成工厂的流水线；
 *     // Java 中提前定义好一些流水线操作；
 *     // 第一步[拿数据流]： 数据流 = 集合.stream()；
 *     // 第二步[中间过程]： 数据流.filter(条件).map(转换).sorted(); //定义数据的流水线过程；
 *     // 第三步[终止操作]： 数据流.collect(收集器); // 把流水线的数据收集起来，最后的结果；
 * @author lfy
 */
public class StreamTest {


    record Person(String name, Integer age){}

    @Test
    public void stream(){
        Person person = new Person("张三", 20);
        System.out.println(person.toString());



    }

    public static void haha(String a){
        System.out.println(a);
    }


    @Test
    public void streamDebug(){
        List<Integer> list = List.of(1, 2, 3, 4, 5,6);


        Consumer consumer = System.out::println;
        Consumer<String> consumer2 = StreamTest::haha;

        consumer2.accept("hahaha");

        // 专门服务流的线程池  百万w级别以上的；分布式计算；慢慢处理
        //  Integer： 4个字节；  100w * 4 = 400M;
        // 9HELLO12HELLO15HELLO18HELLO
        List<String> list1 = list.stream()
                .parallel() //开启并行流； 快速发挥异步能力； 开的线程是forkJoinPool.commonPool();
                .filter(e -> e > 2)
                .map(e -> e * 3)
                .map(e -> e + "hello")
                .flatMap(e -> {
                    String upperCase = e.toUpperCase();
                    String[] split = upperCase.split("");
                    System.out.println(Thread.currentThread() + " " + upperCase);
                    return Stream.of(split);
                })
                .distinct()
                .toList();

        System.out.println(list1);
    }



    /**
     * 大数据也有 MapReduce 功能； 大数据把 Map功能 和 Reduce功能 可以调度给任意机器去来做
     * 分布式计算；
     *
     * Map：     映射： 把一个流中的元素转换成另一个流
     * Reduce：  规约： 把流中的元素总在一起，返回一个值；
     */
    @Test
    public void streamReduce(){
        List<Integer> list = List.of(1, 2, 3, 4, 5,6);


        // Optional: 防止空指针：Null
        Optional<Integer> reduce = list.stream()
                .map(e -> e + 1)
//                .reduce(100, (a, b) -> a + b);
                .reduce((a, b) -> a + b);//BinaryOperator ==> BiFunction ==> R apply(T t， U u);


//        Integer i = reduced.get();

        reduce.ifPresent(System.out::println);
        System.out.println(reduce);
    }


    @Test
    public void streamEnd(){
        List<Integer> list = List.of(1, 2, 3, 4, 5,6);

        // Optional: Java 8 新增的容器类，用于封装可能为null的值；

//        Optional<Integer> max = list.stream()
//                .map(n -> n * 2)
//                .max(Integer::compareTo);
//
//        Integer i = max.get();
//        System.out.println(i);


        List<Integer> collect = list.stream()
                .map(n -> n * 2)
                .filter(n -> n > 8)
                .collect(Collectors.toList());// 老版的 终止操作 收集为List
//                .toList(); //新版的 java16+ 终止操作 收集为List

//        System.out.println(list1);


//        hello(list);
    }


    @Test
    public void streamOperation01(){
        //1、准备数据
        List<Integer> list = List.of(1, 2, 3, 4, 5,6,7,8,9,2,5,7,8,1);

        //2、定义流水线
        // map：(映射)：  高中 函数 y=f(x)： 一一对应


        // 需求： 给每个数字乘以2； 然后再拼接上x字符串
        Stream<String> stream = list.stream()
                .map(m -> m * 2) //产生新的流
                .map(n -> n + "x")
                .flatMap(ele -> {
                    // Function<? super T  ,   ? extends Stream<? extends R>> mapper
                    // Function<T, R>
                    //切分  "10x" 1 0 x
                    String[] split = ele.split("");
                    return Arrays.stream(split); //返回什么要看当时源码的规定，要什么给什么
                }).sorted();// 扁平化， 接受一个参数，返回一个流

        stream.forEach(System.out::println);
        //流一旦遇到终止操作，就不能再用了；


        // 流中有多个元素 ; count() 统计流中元素的数量；终止操作
//        long count = stream.count();
//        System.out.println("总元素："+count);


    }


    @Test
    public void streamOperation(){
        //1、准备数据
        List<Integer> list = List.of(1, 2, 3, 4, 5,6,7,8,9,2,5,7,8,1);

        //2、定义流水线
        Predicate<Integer> predicate = n -> n > 3; //


        // Stream API 新语法； 筛选和切片
        list.stream() //流开始
            .filter(n -> n > 0) //经过过滤；  返回true，代表要，false，代表不要；
            .distinct() // 去重
            .limit(5) // 只取前5个
            .skip(2) // 跳过前两个
            .forEach(System.out::println); //终止操作；



    }



    @Test
    public void testCreateStream01(){
        //1、原生数据； 直接获取流
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 3, 8, 3, 2, 1);
        Stream<Integer> stream = list.stream();


        //2、数组转流
        Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 3, 8, 3, 2, 1};
        Stream<Integer> stream1 = Arrays.stream(arr);
        // 打印集合数据
//        stream1.forEach(System.out::println);


        // 用到循环的索引，自己就写个循环；
        Arrays.stream(arr)
              .forEach(System.out::println);


        //3、Stream.of
        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5);


        //4、生成无限流；  迭代生成流； 比如： 0,2,4,6,8,10...
        Stream<Integer> stream3 = Stream.iterate(0, n -> n + 2);



        //5、打印一下流中的数据  对象::方法引用
        Consumer<Integer> abc = System.out::println;
        System.out.println("stream3 = " + stream3);

//        stream3.forEach(System.out::println);
    }







    public static void main(String[] args) {
        // 没有 Stream API 以前，所有对集合数据的操作都是命令式的，比如：

        List<Integer> list = List.of(1, 2, 3, 4, 5,6,7,8,9,2,5,7,8,1);

        List<Integer> result = new ArrayList<>();
        // 找出 list 中所有的大于3 的数
        for (Integer val : list) {
           if (val > 3) {
               result.add(val);
           }
        }
        System.out.println("结果：" + result);
        result.sort(Integer::compareTo);
        System.out.println("结果：" + result);




    }
}
